#!/usr/bin/python

# Copyright 2009, 
# Fei Yu, feiyu [at] berkeley [dot] edu, 
# Jeffrey Regier, jeff [at] stat [dot] berkeley [dot] edu.
# 

# This file is part of Topics-Portal.
#
# Topics-Portal is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Topics-Portal is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with PyGrams.  If not, see <http://www.gnu.org/licenses/>.

from ims_mysql.sti.models import *
from django.shortcuts import render_to_response, get_object_or_404
from django.http import Http404, HttpResponseRedirect
from django.core.urlresolvers import reverse

SEARCH_LIMIT = 100


def home(request):
	return render_to_response('home.html', {'title': 'Welcome'})


def topic(request, topicName):
	topic = get_object_or_404(Topic, name=topicName)
	def getReferences(topic,ref_type):
		entries = [a.entry for a in Alias.objects.filter(alias=topic)]
		if ref_type == 'book':
			toc = TOC.objects.filter(topic__in=entries)
			books = list(set([row.book for row in toc]))
			references = [(book,
							[row.page for row in toc.filter(book=book).order_by('page') if row.page],)
							for book in books]
		if ref_type == 'lecture':
			covers = Covers.objects.filter(topic__in=entries)
			courses = list(set([c.lecture.course for c in covers]))
			references = [(course,
								sorted([c.lecture for c in covers.filter(lecture__course=course) \
										if c.lecture],cmp=lambda x,y: cmp(int(x.number),int(y.number))))
							for course in courses]
		return references
		
	def getRelated(init_topic):
		related = set([])
		stack = set([(init_topic,None),])
		while len(stack) > 0:
			topic,source = stack.pop()
			related |= set([(topic,source),])
			stack |= set([(ta.from_topic, ta.source,) for ta in topic.inbound.filter(assoc_type=1)]).difference(related)
			stack |= set([(ta.to_topic, ta.source,) for ta in topic.outbound.filter(assoc_type=1)]).difference(related)
		data = []
		distinct_topics = []
		for topic,source in list(related):
			if (topic != init_topic) and (topic not in distinct_topics) and (source != None):
				data.append((topic,source))
				distinct_topics.append(topic)
		return data
		
	context = {
		'topic' : topic,
		'title': topic.name,
		'practitioners': [(p.person, p.source) for p in topic.expert_set.all()],
		'parents': [(ta.from_topic, ta.source,)
					for ta in topic.inbound.filter(assoc_type=0)],
		'children': [(ta.to_topic, ta.source,) for ta in
					 topic.outbound.filter(assoc_type=0)],
		'related' : getRelated(topic),
		'aux_encyclopedia_urls': topic.topicaux_set.filter(url_type__in=range(3)),
		'isi_url': topic.topicaux_set.filter(url_type=3)[:1],
		'references_book': getReferences(topic,'book'),
		'references_lecture': getReferences(topic,'lecture'),
		}
	return render_to_response('topic.html', context)


def topicIndex(request):
	page = request.GET.get('page', 'A')
	context = {
		"topics": Topic.objects.filter(name__istartswith=page).order_by('name'),
		"current_letter": page,
		"title": "Topic Index"}
	return render_to_response('topic_index.html', context)


def search(request):
	query = request.GET['q']
	if Topic.objects.filter(name=query)[:1].count():
		url = reverse('ims_mysql.sti.views.topic', args=(query,))
		return HttpResponseRedirect(url)
	
	approxMatches = Topic.objects.filter(name__search=query)[:SEARCH_LIMIT]
	if not approxMatches:
		approxMatches = Topic.objects.order_by('name').filter(
			name__icontains=query)[:SEARCH_LIMIT]

	if approxMatches:
		context = {'matches': approxMatches}
		return render_to_response('search_results.html', context)
	return render_to_response('no_results.html')


def personIndex(request):
	def data_generator():
		"""
		This is a generator that gets all persons' info with a single query
		and then packs topic names in a list. It fetches all data with a single query,
		so that DB won't get stressed as heavily as if we did a query for every author.
		"""
		knows = Knows.objects.extra(
			select={"first_name": "first_name", "last_name" : "last_name",
					"topic_name": "sti_topic.name"},
			tables=['sti_person', 'sti_topic'],
			where=["person_id = sti_person.id", "topic_id = sti_topic.id"]
			).values('first_name', 'last_name', 'topic_name')
		fn_old, ln_old = None, None
		for k in knows:
			fn, ln, topic = k['first_name'], k['last_name'], k['topic_name']
			if (ln != ln_old) or (fn != fn_old):
				if fn_old and ln_old:
					yield _topics(fn_old, ln_old, topics)
				fn_old, ln_old, topics = fn, ln, set([topic])
			else:
				topics.add(topic)
		else:
			if fn_old and ln_old:
				yield _topics(fn, ln, topics)

	def _topics(fn, ln, topics):
		topics = list(topics)
		topics.sort()
		return fn, ln, topics

	pTopics = Person.objects.nameWithTopics()
	context = {'title': 'Person Index', 'data': data_generator()}
	return render_to_response('person_index.html', context)


def courseIndex(request):
	context = {
		"courses": Course.objects.order_by('-semester','name'),
		"title": "Course Index"}
	return render_to_response('courseIndex.html', context)

def book_data_generator(book):
	## ordering_pos() expects numbers or characters
	def ordering(pos1,pos2):
		min_length = len(pos1) if len(pos1) < len(pos2) else len(pos2)
		for i in range(min_length):
			if pos1[i]==pos2[i]: continue
			is_digit_1=True
			is_digit_2=True
			try: int(pos1[i])
			except: is_digit_1=False
			try: int(pos2[i])
			except: is_digit_2=False
			if is_digit_1 and is_digit_2: return cmp(int(pos1[i]),int(pos2[i]))
			if is_digit_1 and not is_digit_2: return 1
			if not is_digit_1 and is_digit_2: return -1
			return cmp(pos1[i],pos2[i])
		else:
			if len(pos1) > len(pos2): return 1
			if len(pos1) < len(pos2): return -1
		return 0
		
	book_data = []
	toc = TOC.objects.filter(book=book)
	for entry in toc:
		alias = None if len(Alias.objects.filter(entry=entry.topic)) != 1 \
					else Alias.objects.get(entry=entry.topic).alias
		book_data.append([entry.position.split('.'),[(entry.topic,alias)],entry.page]) #([pos],[(topic,alias)],page)
	book_data.sort(cmp=lambda x,y:ordering(x[0],y[0]))
	book_data2 = []
	last_pos = None
	for c in book_data:
		if c[0] == last_pos: 
			book_data2[-1][1] += c[1]
		else:
			book_data2.append(c)
			last_pos = c[0]
	for c in book_data2:
		c[1].sort(cmp=lambda x,y:-cmp(len(x[0].name),len(y[0].name)))		
	return book_data2



def course(request,courseName,choice='lecture'):
	semester,name = courseName.split('&')
	course = get_object_or_404(Course, name=name,semester=semester)
	
	
	def availability(course):
		l_flag = b_flag = False
		if len(Lecture.objects.filter(course=course)) > 0: l_flag=True
		if len(course.books.all()) > 0: b_flag = True
		
		if l_flag and b_flag: return 'both'
		elif l_flag: return 'lecture'
		elif b_flag: return 'book'
		else: return 'none'
	
	def lecture_data_generator(course):
		lectures = Lecture.objects.filter(course=course)
		data = []
		for lecture in lectures:
			topics = []
			lectureTOC = Covers.objects.filter(lecture=lecture).order_by('position')
			for t in [c.topic for c in lectureTOC]:
				alias = None if len(Alias.objects.filter(entry=t)) != 1	\
							else Alias.objects.get(entry=t).alias
				topics.append((t,alias))
			data.append((lecture,topics))
		data.sort(cmp=lambda x,y:cmp(int(x[0].number),int(y[0].number)))
		return data
		
	def get_book_data(course):
		books = course.books.all()
		data = []
		for bk in books:
			data.append((bk,book_data_generator(bk)))
		return data
	
	data = lecture_data_generator(course) if choice=='lecture' else get_book_data(course)

	context = {
		'title': course,
		'choice' : choice,
		'availability' : availability(course),
		'data' : data,
		}
	return render_to_response('course.html',context)
	
def bookIndex(request):
	context = {
		"books": Book.objects.order_by('title','-edition'),
		"title": "Book Index",
		}
	return render_to_response('bookIndex.html', context)
	
def book(request,bookName,):
	title = bookName.split('&')[0]
	if len(bookName.split('&')) == 1: edition = None
	else:	edition= int(bookName.split('&')[1].split('Ed')[0])
	book = get_object_or_404(Book, title=title,edition=edition)
	
	context = {
		'data' : [(book,book_data_generator(book)),],
		'title' : book,
		'courses' : book.course_set.all()
		}
	return render_to_response('book_base.html', context)	

def redirect(request,ext_url):
	return HttpResponseRedirect(ext_url)

def about(request):
	return render_to_response('about.html', {'title': 'About'})

